package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

/*
HTTP协议客户端实现
	Go语言标准库内置了net/http包，涵盖了HTTP客户端和服务端具体的实现方式。无须借助第三方网络通信库，就可以直接使用HTTP中用得最多的GET和POST方式请求数据
*/
func main() {

	/*
		一. 使用http.NewRequest ()方法 实现客户端：
			1. 创建一个client（客户端）对象：http.Client{}；
			2. 创建一个request（请求）对象： http.NewRequest()；
			3. 使用client发送request：client.Do(request)；
	*/

	// 1. 创建一个client（客户端）对象
	requestUrl := "http://localhost:8088/restful" // TIPS：使用了product项目的master分支
	client := http.Client{}

	// 2.1 创建一个 [GET] request（请求）对象
	request, err := http.NewRequest("GET", requestUrl, nil)
	// 检查错误
	checkError(err)
	// 2.2 使用client发送request
	response, err := client.Do(request)
	// 检查错误
	checkError(err)
	defer response.Body.Close()
	// 2.3 查看请求头数据
	fmt.Printf("Header: %v \n", request.Header)
	fmt.Printf("响应状态码：%v \n", response.StatusCode)
	if response.StatusCode == 200 {
		fmt.Println("GET网络请求成功")
		all, err := ioutil.ReadAll(response.Body)
		checkError(err)
		fmt.Println(string(all))

	} else {
		fmt.Println("GET网络请求失败：", response.StatusCode)
	}

	// 3.1 创建一个 [POST] request（Json格式数据请求）对象
	//json序列化
	jsonBody := `{
		"name": "steven",
		"password": "12344567",
		"age": 25
	}`
	jsonStr := []byte(jsonBody)
	request2, err2 := http.NewRequest("POST", requestUrl, bytes.NewBuffer(jsonStr))
	// 检查错误
	checkError(err2)
	// 设置请求头，设置发送json格式数据
	request2.Header.Set("Content-Type", "application/json")
	// 3.2 使用client发送request
	response2, err2 := client.Do(request2)
	// 检查错误
	checkError(err2)
	defer response2.Body.Close()
	// 3.3 查看请求头数据
	fmt.Printf("Header: %v \n", request2.Header)
	fmt.Printf("响应状态码：%v \n", response2.StatusCode)
	if response2.StatusCode == 200 {
		fmt.Println("POST网络请求成功")
		all, err := ioutil.ReadAll(response2.Body)
		checkError(err)
		fmt.Println(string(all))

	} else {
		fmt.Println("POST网络请求失败：", response2.StatusCode)
	}

	// 4.1 创建一个 [PUT] request（Json格式数据请求 + 请求url中获取数据）对象
	//json序列化
	jsonBody2 := `{
		"name": "steven",
		"password": "12344567",
		"age": 25
	}`
	jsonStr2 := []byte(jsonBody2)
	request3, err3 := http.NewRequest("PUT", requestUrl+"/12", bytes.NewBuffer(jsonStr2))
	// 检查错误
	checkError(err3)
	// 设置请求头，设置发送json格式数据
	request3.Header.Set("Content-Type", "application/json")
	// 4.2 使用client发送request
	response3, err3 := client.Do(request3)
	// 检查错误
	checkError(err3)
	defer response3.Body.Close()
	// 4.3 查看请求头数据
	fmt.Printf("Header: %v \n", request3.Header)
	fmt.Printf("响应状态码：%v \n", response3.StatusCode)
	if response3.StatusCode == 200 {
		fmt.Println("PUT网络请求成功")
		all, err := ioutil.ReadAll(response3.Body)
		checkError(err)
		fmt.Println(string(all))

	} else {
		fmt.Println("PUT网络请求失败：", response3.StatusCode)
	}

	// 5.1 创建一个 [DELETE] request（请求url中获取数据）对象
	request5, err5 := http.NewRequest("DELETE", requestUrl+"/12", nil)
	// 检查错误
	checkError(err5)
	// 5.2 使用client发送request
	response5, err5 := client.Do(request5)
	// 检查错误
	checkError(err5)
	defer response5.Body.Close()
	// 5.3 查看请求头数据
	fmt.Printf("Header: %v \n", request5.Header)
	fmt.Printf("响应状态码：%v \n", response5.StatusCode)
	if response5.StatusCode == 200 {
		fmt.Println("DELETE网络请求成功")
		all, err := ioutil.ReadAll(response5.Body)
		checkError(err)
		fmt.Println(string(all))

	} else {
		fmt.Println("DELETE网络请求失败：", response5.StatusCode)
	}

	/*
		二. 使用client. Get()方法。步骤如下：
			1. 创建一个client（客户端）对象；
			2. 使用client调用Get()方法
	*/
	resp, err := client.Get(requestUrl)
	checkError(err)
	if resp.StatusCode == 200 {
		fmt.Println("client.Get()请求成功")
		defer resp.Body.Close()
		all, err := ioutil.ReadAll(resp.Body)
		checkError(err)
		fmt.Println(string(all))
	}

	/*
		三. 使用http. Get()方法
			TIPS：这种方式只有一个步骤，http的Get()方法就是对DefaultClient.Get()的封装
	*/
	get, err := http.Get(requestUrl)
	checkError(err)
	if get.StatusCode == 200 {
		fmt.Println("http.Get()请求成功")
		defer func(Body io.ReadCloser) {
			err := Body.Close()
			if err != nil {

			}
		}(get.Body)
		all, err := ioutil.ReadAll(get.Body)
		checkError(err)
		fmt.Println(string(all))
	}

	/*
		四. 使用client. Post()或client.PostForm()方法。步骤如下：
			1. 创建一个client（客户端）对象
			2. 使用client调用Post()或PostForm()方法
	*/
	// client. Post()
	post, err := client.Post(requestUrl, "application/json", bytes.NewBuffer(jsonStr))
	checkError(err)
	if post.StatusCode == 200 {
		fmt.Println("client. Post()请求成功")
		defer func(Body io.ReadCloser) {
			err := Body.Close()
			if err != nil {

			}
		}(post.Body)
		all, err := ioutil.ReadAll(post.Body)
		checkError(err)
		fmt.Println(string(all))
	}
	// client.PostForm()
	values := make(url.Values)
	values["name"] = []string{"steven"}
	values["password"] = []string{"1234567"}
	values["age"] = []string{"18"}
	form, err := client.PostForm(requestUrl+"/from", values)
	checkError(err)
	if form.StatusCode == 200 {
		fmt.Println("client. PostForm()请求成功")
		defer func(Body io.ReadCloser) {
			err := Body.Close()
			if err != nil {

			}
		}(form.Body)
		all, err := ioutil.ReadAll(form.Body)
		checkError(err)
		fmt.Println(string(all))
	}

	/*
		五. 使用http. Post()或http.PostForm()方法
			TIPS：http的Post()函数或PostForm()，就是对DefaultClient.Post()或DefaultClient.PostForm()的封装。这种方法只需要一个步骤
	*/
	// http. Post()
	r, err := http.Post(requestUrl, "application/json", bytes.NewBuffer(jsonStr))
	checkError(err)
	if r.StatusCode == 200 {
		fmt.Println("http. Post()请求成功")
		defer func(Body io.ReadCloser) {
			err := Body.Close()
			if err != nil {

			}
		}(r.Body)
		all, err := ioutil.ReadAll(r.Body)
		checkError(err)
		fmt.Println(string(all))
	}
	// http.PostForm()
	postForm, err := http.PostForm(requestUrl+"/from", values)
	checkError(err)
	if postForm.StatusCode == 200 {
		fmt.Println("http.PostForm()请求成功")
		defer func(Body io.ReadCloser) {
			err := Body.Close()
			if err != nil {

			}
		}(postForm.Body)
		all, err := ioutil.ReadAll(postForm.Body)
		checkError(err)
		fmt.Println(string(all))
	}
}

/*
检查错误函数
*/
func checkError(err error) {

	defer func() {
		// recover()可以让进入恐慌流程的Goroutine（可当作线程理解）恢复过来并重新获得流程控制权。专用于“拦截”运行时panic
		if e, ok := recover().(error); ok {
			fmt.Println("程序异常：", e.Error())
		}
	}()
	if err != nil {
		// 主动panic
		panic(err)
	}
}
